﻿/**
 * Copyright (C) 2011 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Internal.Util;
using System.Collections;

namespace NGuice.Inject.Spi
{
    //complete!
    /// <summary>
    /// 用于表示所有依赖的泛型依赖的标记接口,由于C#中没有泛型通配符，因此使用标记接口解决通配泛型问题
    /// </summary>
    public interface Dependency 
    { 
        /// <summary>
        /// 获取适合此依赖的绑定key
        /// </summary>
        Key Key
        {
            get;
        }

        /// <summary>
        /// 如果此依赖允许null值
        /// </summary>
        bool IsNullable
        {
            get;
        }

        /// <summary>
        /// 获取此依赖所属的注入点，如果此依赖不关联一个特定的注入点，则为null
        /// </summary>
        InjectionPoint InjectionPoint
        {
            get;
        }

        /// <summary>
        /// 获取此依赖在注入点参数列表中的索引，如果此依赖不属于一个参数列表，则为-1。
        /// <remarks>只有方法或构造依赖才是参数列表的元素</remarks>
        /// </summary>
        int ParameterIndex
        {
            get;
        }
    }

    /// <summary>
    /// 能够被一个注入器解析的一个变量。使用Get构建一个独立的依赖或者使用InjectionPoint构建关联于一个构造，方法或者域的依赖
    /// </summary>
    /// <typeparam name="T">表示在此类型上的依赖</typeparam>
    public sealed class Dependency<T>: Dependency
    {
        private readonly InjectionPoint injectionPoint;

        private readonly Key<T> key;

        private readonly bool nullable;

        private readonly int parameterIndex;

        public Dependency(InjectionPoint injectionPoint, Key<T> key, bool nullable, int parameterIndex)
        {
            this.injectionPoint = injectionPoint;
            this.key = Preconditions.CheckNotNull<Key<T>>(key, "key");
            this.nullable = nullable;
            this.parameterIndex = parameterIndex;
        }

        /// <summary>
        /// 返回一个不关联于注入点的依赖，返回的依赖是可为null的
        /// </summary>
        public static Dependency<E> Get<E>(Key<E> key)
        {
            return new Dependency<E>(null, key, true, -1);
        }

        /// <summary>
        /// 非泛型方法，传递的key和返回的Dependency都必须是泛型类
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static Dependency GetForNoGeneric(Key key)
        {
            if (!key.GetType().IsGenericType)
                throw new ArgumentException("the key must be generic type");
            dynamic k = key;
            return Get(k);//TODO:原本使用反射调用泛型方法返回，现在使用动态对象方式调用会更好，但需要经过测试是否可行!
        }

        /// <summary>
        /// 从给定的注入点集返回对应的依赖集，类型参数由于无法使用通配符，只能使用标记接口
        /// </summary>
        public static ISet<Dependency> ForInjectionPoints(ISet<InjectionPoint> injectionPoints)
        {
            List<Dependency> dependencies = Lists.NewArrayList<Dependency>();
            foreach (InjectionPoint injectionPoint in injectionPoints)
            {
                foreach (Dependency o in injectionPoint.Dependencies)
                {
                    dependencies.Add(o);
                }
            }
            return ImmutableSet<Dependency>.CopyOf<Dependency, Dependency>(dependencies);
        }

        /// <summary>
        /// 获取适合此依赖的绑定key
        /// </summary>
        public Key<T> Key
        {
            get
            {
                return this.key;
            }
        }

        Key Dependency.Key
        {
            get { return Key; }
        }

        /// <summary>
        /// 如果此依赖允许null值
        /// </summary>
        public bool IsNullable
        {
            get
            {
                return nullable;
            }
        }

        /// <summary>
        /// 获取此依赖所属的注入点，如果此依赖不关联一个特定的注入点，则为null
        /// </summary>
        public InjectionPoint InjectionPoint
        {
            get
            {
                return injectionPoint;
            }
        }

        /// <summary>
        /// 获取此依赖在注入点参数列表中的索引，如果此依赖不属于一个参数列表，则为-1。
        /// <remarks>只有方法或构造依赖才是参数列表的元素</remarks>
        /// </summary>
        public int ParameterIndex
        {
            get
            {
                return parameterIndex;
            }
        }

        public override int GetHashCode()
        {
            return Objects.GetHashCode(injectionPoint, parameterIndex, key);
        }

        public override bool Equals(object o)
        {
            if (o is Dependency)
            {
                dynamic dependency = o;
                return Objects.Equal(injectionPoint, dependency.injectionPoint)
                    && Objects.Equal(parameterIndex, dependency.parameterIndex)
                    && Objects.Equal(key, dependency.key);
            }
            else
            {
                return false;
            }
        }

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(key);
            if (injectionPoint != null)
            {
                builder.Append("[").Append(injectionPoint).Append("]");
                if (parameterIndex != -1)
                {
                    builder.Append("(").Append(parameterIndex).Append(")");
                }
            }
            return builder.ToString();
        }

    }
}
